前面花了很多篇幅在 Verilog 語法的說明和 IC 設計的概念。從這篇開始,我們要開始來接觸 RISC-V 架構。首先我們先來認識其中的 R-Type 指令。R
代表的是 Register
,也就是說 R-Type 指令主要描述的是暫存器之間的運算關係。
RISC-V 的指令都是 32 位元寬,因此接下來介紹的所有指令都需要 32 位元來表示。不同的 Type 差別在,這 32 位元中,不同的位置負責表達不同的資料,而這篇文章的目標就是了解 R-Type 中,各個位元表達的含義。
31 : 25 | 24 : 20 | 19 : 15 | 14 : 12 | 11 : 7 | 6 : 0 |
---|---|---|---|---|---|
funct7 | rs2 | rs1 | funct3 | rd | opcode |
R-Type 指令包含了 opcode
, function
, rd
, rs1
, rs2
,以下我們詳細說明這些資料的意義。
opcode
: 這個資料代表指令代表的 Type ,以 R-Type 來說,opcode 必定為 0110011
。function
: 即便確認了指令屬於 R-Type ,我們仍不知道確切的功能,此時就要透過 funct7
, funct3
來決定,透過 function 我們可以確定指令代表的功能為何。
為什麼 function 要被拆成兩個部分呢?因為 RISC-V 需要快速的進行指令解碼,因此所有指令的 opcode, rd, rs 都會被安排在相同位置。而剩餘的空間並不足以塞下完整的 function,所以才將他分成兩個部分。
rd
: 這個資料代表目的暫存器 (destination)。當運算完畢後,結果會被存入這個暫存器。rs1
, rs2
: 這兩個資料都是來源暫存器 (source)。需要被運算的資料都會被存入來源暫存器。小結論:假設當前指定的運算功能為 @
,則 R-Type 指令的含義即為 rd = rs1 @ rs2
。
以下列出所有 R-Type 指令,這些規範可以從 RISC-V 官網上找到喔!
funct7 | rs2 | rs1 | funct3 | rd | opcode | 指令名稱 | 功能 |
---|---|---|---|---|---|---|---|
0000000 | rs2 | rs1 | 000 | rd | 0110011 | ADD | rd = rs1 + rs2 |
0100000 | rs2 | rs1 | 000 | rd | 0110011 | SUB | rd = rs1 - rs2 |
0000000 | rs2 | rs1 | 001 | rd | 0110011 | SLL | rd = rs1 << rs2 |
0000000 | rs2 | rs1 | 010 | rd | 0110011 | SLT | rd = rs1 < rs2 |
0000000 | rs2 | rs1 | 011 | rd | 0110011 | SLTU | rd = rs1 < rs2 |
0000000 | rs2 | rs1 | 100 | rd | 0110011 | XOR | rd = rs1 ^ rs2 |
0000000 | rs2 | rs1 | 101 | rd | 0110011 | SRL | rd = rs1 >> rs2 |
0100000 | rs2 | rs1 | 101 | rd | 0110011 | SRA | rd = rs1 >>> rs2 |
0000000 | rs2 | rs1 | 110 | rd | 0110011 | OR | rd = rs1 | rs2 |
0000000 | rs2 | rs1 | 111 | rd | 0110011 | AND | rd = rs1 & rs2 |
R-Type 指令主要負責「算術邏輯運算」,其中比較特別的是「位移」和「比大小」。
位移根據方向可以分成左移 (Left) 和右移 (Right),根據性質則可以分成邏輯位移 (Logical) 和算術位移 (Arithmetic) 。雖然能得出四種組合,但是邏輯左移和算術左移的結果是相同的,因此只保留「邏輯左移」,也就是 SLL (Shift Left Logical) 。
邏輯位移指的是當一個數轉為「位元」表示時,因為位移而產生的空缺都以 0
來填補,符號上以 >>
表示邏輯右移、<<
表示邏輯左移。舉幾個例子:
5 << 2 = 20
- 因為 5 的二進位表示為
101
,向左位移 2 位可以得到101xx
,由於是邏輯位移,所以使用0
來填補空缺得到結果10100
,也就是 20- 若左移的結果超過資料能表示的最大值,則稱為 overflow ,此時超出的位元都將被省略
n << m
等同於n * 2ᵐ
9 >> 3 = 1
- 因為 9 的二進位表示為
1001
,向右位移 3 位可以得到xxx1
,由於是邏輯位移,所以使用0
來填補空缺得到結果0001
,也就是 1- 特別注意的是被右移移除的位元需直接省略
n >> m
等同於floor(n / 2ᵐ)
算數位移指的是當一個數轉為「位元」表示時,因為位移而產生的空缺都以 Sign Bit 來填補。如此一來,不論是正數還是負數,在位移後都能保持其正負號特性。若想知道更詳細的原因,可以參考「二補數法」。算術位移我們只討論右移,其符號為 >>>
。舉兩個例子:
9 >>> 3 = 1
- 因為 9 的二進位表示為
1001
,向右位移 3 位可以得到xxx1
,由於是算術位移,再加上 Sign Bit 為 0 ,所以使用0
來填補空缺得到結果0001
,也就是 1- 特別注意的是 Sign Bit 指的是資料的 MSB ,因為目前使用的是 RV32 ,因此要查看第 32 位元的值
-5 >>> 3 = -1
- 因為 -5 的二進位表示為
11...11111011
,向右位移 3 位可以得到xxx11...11111
,由於是算術位移,再加上 Sign Bit 為 1 ,所以使用1
來填補空缺得到結果11111...11111
,也就是 -1
SLT
, SLTU
是其中較特別的指令。SLT
的全名為 Set Less Than ,而 SLTU
的全名則為 Set Less Than Unsigned ,兩者的差異是比較大小時,以「無號數」還是「有號數」。若運算結果為 rs1 < rs2
則回傳 1
,否則為 0
。
R-Type 指令是指令集中最直觀的部分,下一篇我們要接著介紹 I-Type 的指令,概念和 R-Type 相當類似。